home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1997 August / Walnut Creek CDROM.7z / VOL_400 / 453_01 / EXAMPLES / DISCAT / CATALOG.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-27  |  6.2 KB  |  223 lines

  1. //    catalog.cpp  -  recursive directory scanner sample code  -  1.2
  2. //
  3. //    This is a part of the MetaKit library.
  4. //    Copyright (c) 1996 Meta Four Software.
  5. //    All rights reserved.
  6. /////////////////////////////////////////////////////////////////////////////
  7. //
  8. //    The following two globally accesible routines are defined below:
  9. //                    
  10. //        c4_View fScanDirectories(const char* path_);
  11. //        CString fFullPath(c4_View& dirs_, int dirNum_);
  12. //        
  13. //    The fScanDirectories routine does all the work, and completely hides all
  14. //    Windows 16/32 specifics by returning a generalized catalog tree object.
  15. //    In contrast with normal C++ conventions, nearly all MetaKit objects are
  16. //    reference counted. This takes care of fully automatic cleanup after use.
  17. //    
  18. //    The structure of the object returned by fScanDirectories is as follows:
  19. //    
  20. //        Property        Type        Description
  21. //        ========        ====        ===========
  22. //            
  23. //        dirs            nested        Contains 1 record per directory
  24. //            parent        integer        Index of parent entry
  25. //            name        string        Name of this directory
  26. //            files        nested        Contains 1 record per file entry
  27. //                name    string        Name of this file
  28. //                size    string        File size
  29. //                date    integer        Modification date (DOS format 7+4+5 bits)
  30. //    
  31. //    The first directory entry (entry 0) is special: the parent is set to zero
  32. //    (points to itself), and the name is the base path name (see path_ arg).
  33. //                
  34. //    In C++ notation, a declaration for this object would be something like:
  35. //    
  36. //        struct
  37. //        {
  38. //            int parent;
  39. //            char* name;
  40. //    
  41. //            struct
  42. //            {
  43. //                char* name;
  44. //                long size;
  45. //                int date;
  46. //    
  47. //            } files [];                    // variable size, not allowed in C++
  48. //    
  49. //        } dirs [];
  50. //
  51. /////////////////////////////////////////////////////////////////////////////
  52.  
  53. #include "stdafx.h"
  54. #include "catalog.h"
  55.  
  56.     // forward declaration
  57. static void ScanSubDir(c4_View&, int, const CString&);
  58.  
  59. /////////////////////////////////////////////////////////////////////////////
  60. // Property definitions
  61.  
  62.     c4_ViewProp        pFiles ("files");
  63.     c4_IntProp        pParent ("parent"),
  64.                     pSize ("size"),
  65.                     pDate ("date");
  66.     c4_StringProp    pName ("name");
  67.  
  68. /////////////////////////////////////////////////////////////////////////////
  69. // Scan a directory tree and return a corresponding structure for it
  70.  
  71. c4_View fScanDirectories(const char* path_)
  72. {
  73.         // Start with a view containing the root directory entry
  74.     c4_View dirs;
  75.     dirs.Add(pName [path_]);
  76.     
  77.         // This loop "automagically" handles the recursive traversal of all
  78.         // subdirectories. The trick is that each scan may add new entries
  79.         // at the end, causing this loop to continue (GetSize() changes!).
  80.         
  81.     for (int i = 0; i < dirs.GetSize(); ++i)
  82.     {
  83.         CString path = fFullPath(dirs, i);
  84.  
  85.         TRACE("Scanning '%s' ...\n", (const char*) path);
  86.         ScanSubDir(dirs, i, path);
  87.     }
  88.     
  89.         // The returned object contains the entire directory tree.
  90.         // Everything is automatically destroyed when no longer referenced.    
  91.     return dirs;
  92. }
  93.  
  94. /////////////////////////////////////////////////////////////////////////////
  95. // Reconstruct the full path name from a subdirectory index in the tree
  96.  
  97. CString fFullPath(c4_View& dirs_, int dirNum_)
  98. {
  99.         // Prefix all parent dir names until the root level is reached
  100.     CString path;
  101.     for (;;)
  102.     {
  103.         path = pName (dirs_[dirNum_]) + "\\" + path;
  104.  
  105.         if (dirNum_ == 0)
  106.             return path; // this result always has a trailing backslash
  107.             
  108.         dirNum_ = (int) pParent (dirs_[dirNum_]);
  109.     }
  110. }
  111.  
  112. /////////////////////////////////////////////////////////////////////////////
  113. // There are two versions of ScanSubDir, one for Win32 and one for Win16
  114.  
  115. #ifdef _WIN32
  116.  
  117.         // Convert a Win32 filedate back to the good old DOS format
  118.     static WORD DosDate(FILETIME& ft)
  119.     {
  120.         SYSTEMTIME st;
  121.         
  122.         if (!FileTimeToSystemTime(&ft, &st))
  123.             return 0;
  124.             
  125.         return (WORD) (((st.wYear-1980) << 9) | (st.wMonth << 5) | st.wDay);
  126.     }
  127.     
  128.         // Scan subdirectory and update the directory structure
  129.     static void ScanSubDir(c4_View& dirs_, int dirNum_, const CString& path_)
  130.     {
  131.         WIN32_FIND_DATA fd;
  132.         
  133.         HANDLE h = FindFirstFile(path_ + "*", &fd);
  134.         if (h)
  135.         {
  136.                 //    Some notes on efficiency:
  137.                 //
  138.                 //    1)  It is probably faster to fill a view with data, and
  139.                 //        then store it in a persistent field, than to modify
  140.                 //        persistent structures in place. In this example, this
  141.                 //        is needed anyway, since we also sort all filenames.
  142.                 //
  143.                 //    2)  If possible, avoid constructing rows inside a loop.
  144.                 //        For that reason, both 'prop [const]' and 'row + row'
  145.                 //        are relatively inefficient.
  146.  
  147.         //    c4_View files = pFiles (dirs_[dirNum_]);
  148.             c4_View files;
  149.             c4_Row dir, file;
  150.  
  151.             do
  152.             {
  153.                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  154.                 {
  155.                     if (fd.cFileName[0] == '.')
  156.                         continue;
  157.  
  158.                 //    dirs_.Add(pParent [dirNum_] + pName [fd.cFileName]);
  159.                     pParent (dir) = dirNum_;
  160.                     pName (dir) = fd.cFileName;
  161.                     dirs_.Add(dir);
  162.                 }            
  163.                 else
  164.                 {
  165.                 //    files.Add(pName [fd.cFileName] + pSize [fd.nFileSizeLow]
  166.                 //                + pDate [DosDate(fd.ftLastWriteTime)]);
  167.                     pName (file) = fd.cFileName;
  168.                     pSize (file) = fd.nFileSizeLow;
  169.                     pDate (file) = DosDate(fd.ftLastWriteTime);
  170.                     files.Add(file);
  171.                 }
  172.                 
  173.             } while (FindNextFile(h, &fd));
  174.             
  175.             pFiles (dirs_[dirNum_]) = files.SortOn(pName);
  176.             
  177.             FindClose(h);
  178.         }
  179.     }
  180.  
  181. #else
  182.  
  183.     #include <dos.h>
  184.     
  185.         // Scan subdirectory and update the directory structure
  186.     static void ScanSubDir(c4_View& dirs_, int dirNum_, const CString& path_)
  187.     {
  188.         _find_t fd;
  189.         
  190.         if (_dos_findfirst(path_ + "*.*", _A_SUBDIR, &fd) == 0)
  191.         {
  192.             c4_View files;
  193.             c4_Row dir, file;
  194.  
  195.             do
  196.             {
  197.                 if (fd.attrib & _A_SUBDIR)
  198.                 {
  199.                     if (fd.name[0] == '.')
  200.                         continue;
  201.  
  202.                     pParent (dir) = dirNum_;
  203.                     pName (dir) = fd.name;
  204.                     dirs_.Add(dir);
  205.                 }            
  206.                 else
  207.                 {
  208.                     pName (file) = fd.name;
  209.                     pSize (file) = fd.size;
  210.                     pDate (file) = fd.wr_date;
  211.                     files.Add(file);
  212.                 }
  213.                 
  214.             } while (_dos_findnext(&fd) == 0);
  215.             
  216.             pFiles (dirs_[dirNum_]) = files.SortOn(pName);
  217.         }
  218.     }
  219.  
  220. #endif
  221.     
  222. /////////////////////////////////////////////////////////////////////////////
  223.